package com.xbongbong.saas.help;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.constant.ConfigConstant;
import com.xbongbong.paas.constant.MessageConstant;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.constant.SizeConstant;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.domain.entity.ext.ConditionsEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.elasticsearch.model.PaasFormDataEsModel;
import com.xbongbong.paas.enums.ConditionEnum;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.enums.PaySheetTypeEnum;
import com.xbongbong.paas.enums.SheetTypeEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.help.CommonHelp;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.pojo.IdNamePojo;
import com.xbongbong.paas.pojo.dto.FormDataListDTO;
import com.xbongbong.paas.pojo.vo.PaasFormDataEsListVO;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.helper.PageHelper;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.pro.domain.entity.UpdateDataEntity;
import com.xbongbong.pro.enums.errorcodes.PayErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.PaymentErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.service.toolbox.statistic.help.FormHelp;
import com.xbongbong.pro.sheet.pojo.AmountDetailGetPojo;
import com.xbongbong.pro.sheet.pojo.dto.SheetAmountDetailGetDTO;
import com.xbongbong.pro.sheet.pojo.vo.SheetAmountDetailGetVO;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ErrorMessageConstant;
import com.xbongbong.saas.domain.entity.ext.PayPlanEntityExt;
import com.xbongbong.saas.domain.entity.ext.PayPlanSheetEntityExt;
import com.xbongbong.saas.enums.RedundantTemplateTypeEnum;
import com.xbongbong.saas.enums.SheetAloneEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.PayPlanEnum;
import com.xbongbong.saas.enums.business.PaySheetEnum;
import com.xbongbong.saas.enums.business.PurchaseEnum;
import com.xbongbong.saas.enums.dictionary.HasRedEnum;
import com.xbongbong.saas.enums.dictionary.PayPlanStatusEnum;
import com.xbongbong.saas.model.PayPlanModel;
import com.xbongbong.saas.model.PayPlanSheetModel;
import com.xbongbong.saas.toolbox.help.FastJsonHelper;
import org.apache.commons.collections4.CollectionUtils;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * @author: 汪腾伟
 * @date: Created in 2021/9/15 15:27
 * @description： 适配工作流这边的付款计划帮助类
 */
@Component
public class SaasPayPlanHelper {

    private static final Logger LOG = LoggerFactory.getLogger(InitDataHelp.class);

    @Resource
    private FundHelp fundHelp;
    @Resource
    private FormHelp formHelp;
    @Resource
    private PayPlanSheetModel payPlanSheetModel;
    @Resource
    private SaasPaySheetHelper saasPaySheetHelper;
    @Resource
    private PaasFormDataEsModel paasFormDataEsModel;
    @Resource
    private CommonHelp commonHelp;
    @Resource
    private PaasFormExplainModel paasFormExplainModel;
    @Resource
    private PayPlanModel payPlanModel;

    /**
     * 通过采购合同ids获取其关联的付款计划（未收、部分未收，或者已收、部分已收）
     * 当创建已核销、预付款核销、坏账时，要获取【未收、部分未收】的付款计划，付款计划不存在时，获取的是【采购合同未收金额】
     * 当红冲已核销、红冲预付款时，要获取原付款单redId关联的【已收、部分已收】的付款计划，付款计划不存在时，获取的是【采购合同已收金额】
     * 预付款、红冲预付款没有关联采购合同，不能调用该接口
     *
     * （1）如果采购合同下有付款计划，则返回采购合同dataIdList对应的所有付款计划list
     * （2）如果采购合同下无付款计划，则只返回采购合同本身（及其未收金额或采购合同金额-红冲需要采购合同已收金额）
     * （3）如果redId不为空，则要获取该关联原始付款单的已红冲部分，从而算出可红冲金额
     * /detail/tab/payment/get
     * @param sumFlag true 只算总额（总未收或总已收），false 封装所有明细
     * @param sheetAmountDetailGetDTO
     * @return SheetAmountDetailGetVO
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public SheetAmountDetailGetVO payAmountDetailGet(boolean sumFlag, SheetAmountDetailGetDTO sheetAmountDetailGetDTO) throws XbbException {
        Integer businessType = sheetAmountDetailGetDTO.getBusinessType();
        String corpid = sheetAmountDetailGetDTO.getCorpid();
        List<Long> dataIdList = sheetAmountDetailGetDTO.getDataIdList();
        PaySheetTypeEnum paySheetTypeEnum = PaySheetTypeEnum.getByBusinessType(businessType);
        boolean isRedFlag = PaySheetTypeEnum.isRed(paySheetTypeEnum.getCode());
        //默认读取付款计划/采购合同的未付款，但是红冲已核销、红冲预付款核销时需要读取付款计划/采购合同的已付（红冲预付款不会关联采购合同和付款计划，不会调用该接口）
        PayPlanEnum payPlanEnum = PayPlanEnum.UN_AMOUNT;
        PurchaseEnum purchaseEnum = PurchaseEnum.UN_FINISH_AMOUNT;
        if (isRedFlag) {
            payPlanEnum = PayPlanEnum.REAL_AMOUNT;
            purchaseEnum = PurchaseEnum.FINISH_AMOUNT;
        }
        if (sumFlag) {
            //只计算总额，不用封装明细，但是要从每条付款计划内求和得到已付/未付总额
            return getResult4AmountDetailSum(isRedFlag, sheetAmountDetailGetDTO, dataIdList, payPlanEnum, purchaseEnum, corpid);
        } else {
            //除了得到总额，还要处理明细-按顺序封装明细内所有东西：签订人、金额、采购合同编号、付款计划编号等
            return getResult4AmountDetail(corpid, isRedFlag, sheetAmountDetailGetDTO, dataIdList, payPlanEnum, purchaseEnum);
        }
    }

    /**
     * 只计算总的未付/已付金额即可，不用封装明细，但是要从每条付款计划内求和得到已付/未付总额（付款单保存验证金额时，需要获取总金额进行数值校验）
     * @param isRedFlag 是否红冲，如果红冲则读取关联原始付款单相关（红冲预付款不会关联采购合同和付款计划，不会调用该接口）
     * @param sheetAmountDetailGetDTO 获取付款单明细dto
     * @param dataIdList 采购合同id集合
     * @param payPlanEnum 未付/实付字段
     * @param purchaseEnum 未付/已付字段
     * @param corpid 公司id
     * @return SheetAmountDetailGetVO
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private SheetAmountDetailGetVO getResult4AmountDetailSum(boolean isRedFlag, SheetAmountDetailGetDTO sheetAmountDetailGetDTO, List<Long> dataIdList, PayPlanEnum payPlanEnum, PurchaseEnum purchaseEnum, String corpid) throws XbbException {
        SheetAmountDetailGetVO sheetAmountDetailGetVO = new SheetAmountDetailGetVO();
        //封装查询付款计划（未付或已付）的条件，如果是红冲的，还需要获得关联的原始付款单
        AmountDetailGetPojo amountDetailGetPojo = getPaymentAmountDetailGetPojo(isRedFlag, sheetAmountDetailGetDTO);
        if (isRedFlag) {
            double redAmount = saasPaySheetHelper.sumRedAmountBySheet(corpid, sheetAmountDetailGetDTO.getLinkPreId());
            amountDetailGetPojo.setRedAmount(redAmount);
            fundHelp.subRedAmount(XbbRefTypeEnum.PAY_SHEET, sheetAmountDetailGetVO, amountDetailGetPojo);
        } else {
            // 得到付款计划（未付/已付）查询结果：只求总的未付/已付，则无需排序，因此explainMap、sortMap都传null
            List<PaasFormDataEntityExt> planList = getPlanList4AmountDetailGet(amountDetailGetPojo, sheetAmountDetailGetDTO, null, null);
            //拥有付款计划的采购合同id集合
            List<Long> hasPaymentCids = new ArrayList<>();
            Double sumAmount = 0D;
            for (PaasFormDataEntityExt entityExt : planList) {
                JSONObject data = entityExt.getData();
                Long purchaseId = data.getLongValue(PayPlanEnum.LINK_PURCHASE.getAttr());
                hasPaymentCids.add(purchaseId);
                //已付或未付金额
                Double amount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, payPlanEnum.getAttr(), 0D);
                sumAmount = Arith.add(sumAmount, amount);
            }
            //移除所有有付款计划的合同ids，剩余无付款计划的采购合同ids
            dataIdList.removeAll(hasPaymentCids);
            if (dataIdList.size() > 0) {
                List<String> fieldList = Arrays.asList(PurchaseEnum.getAttrConnectData(PurchaseEnum.TOTAL_MONEY), PurchaseEnum.getAttrConnectData(PurchaseEnum.FINISH_AMOUNT), PurchaseEnum.getAttrConnectData(PurchaseEnum.UN_FINISH_AMOUNT));
                List<PaasFormDataEntityExt> contracts = formHelp.getFormListByIds4All(corpid, IndexTypeEnum.IDX_SAAS_PURCHASE, dataIdList, fieldList);
                for (PaasFormDataEntityExt entityExt : contracts) {
                    //已付或未付金额
                    Double amount = FastJsonHelper.getDoubleOrDefaultFromFormData(entityExt.getData(), purchaseEnum.getAttr(), 0D);
                    sumAmount = Arith.add(sumAmount, amount);
                }
            }
            sheetAmountDetailGetVO.setSumAmount(sumAmount);
        }
        return sheetAmountDetailGetVO;
    }

    /**
     * 非红冲：则封装查询付款计划（未付或已付）的条件
     * 红冲：还需要获得关联的原始付款单
     * @param isRedFlag 是否红冲，如果红冲则读取关联原始付款单相关（红冲预付款不会关联采购合同和付款计划，不会调用该接口）；否则读取付款计划的未付款
     * @param sheetAmountDetailGetDTO 获取付款单明细dto
     * @return AmountDetailGetPojo
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public AmountDetailGetPojo getPaymentAmountDetailGetPojo(boolean isRedFlag, SheetAmountDetailGetDTO sheetAmountDetailGetDTO) throws XbbException {
        AmountDetailGetPojo amountDetailGetPojo = new AmountDetailGetPojo();
        List<ConditionsEntityExt> conditionList = new ArrayList<>();
        //计划状态处理（如果是红冲，还需要读取原单、及原单已红冲金额，用于计算原单可红冲金额）
        ConditionsEntityExt planStatus;
        String corpid = sheetAmountDetailGetDTO.getCorpid();
        if (isRedFlag) {
            //红冲付款单，则需要传入关联原始付款单（红冲预付款余额不需要关联原单，且也不会调用该接口
            Long linkPreId = sheetAmountDetailGetDTO.getLinkPreId();
            if (Objects.equals(linkPreId, null) || Objects.equals(linkPreId, BasicConstant.ZERO_LONG)) {
                throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208040, PaymentErrorCodeEnum.API_ERROR_208040.getMsg(), XbbRefTypeEnum.PAY_SHEET.getName());
            }
            //关联的原始付款单
            PayPlanSheetEntityExt linkSheetEntity = payPlanSheetModel.getByKey(linkPreId, corpid);
            if (Objects.isNull(linkSheetEntity) || Objects.equals(linkSheetEntity.getDel(), BasicConstant.ONE)) {
                throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208045, PaymentErrorCodeEnum.API_ERROR_208045.getMsg(), XbbRefTypeEnum.PAY_SHEET.getName());
            }
            amountDetailGetPojo.setLinkSheetEntity(linkSheetEntity);
            //读取已付、部分付款
            planStatus = new ConditionsEntityExt(PayPlanEnum.STATUS.getAttr(), "", PayPlanEnum.STATUS.getFieldType(), ConditionEnum.IN.getSymbol(), Arrays.asList(PayPlanStatusEnum.RECEIVABLES.getCode(), PayPlanStatusEnum.PARTIAL_RECEIPTS.getCode()));
        } else {
            //读取未付款、部分付款
            planStatus = new ConditionsEntityExt(PayPlanEnum.STATUS.getAttr(), "", PayPlanEnum.STATUS.getFieldType(), ConditionEnum.IN.getSymbol(), Arrays.asList(PayPlanStatusEnum.UN_RECEIVABLES.getCode(), PayPlanStatusEnum.PARTIAL_RECEIPTS.getCode()));
            //有未付金额的付款计划，如果付款计划的未付金额为0，则不能选择到付款计划。因为创建付款单时要把付款计划的未付款减少
            conditionList.add(new ConditionsEntityExt(PayPlanEnum.UN_AMOUNT.getAttr(), "", PayPlanEnum.UN_AMOUNT.getFieldType(), ConditionEnum.GREATERTHAN.getSymbol(), Collections.singletonList(BasicConstant.ZERO_DOUBLE)));
        }
        conditionList.add(planStatus);
        //塞入采购合同ids条件
        List<Object> contractIdList = new ArrayList<>(sheetAmountDetailGetDTO.getDataIdList());
        ConditionsEntityExt conditionsEntityExt = new ConditionsEntityExt(PayPlanEnum.LINK_PURCHASE.getAttr(), "", PayPlanEnum.LINK_PURCHASE.getFieldType(), ConditionEnum.IN.getSymbol(), contractIdList);
        conditionList.add(conditionsEntityExt);
        //paymentIdList为空，则返付采购合同关联的所有付款计划（无计划则返回采购合同本身）；该值不为空，则只展示该paymentIdList内的付款计划
        List<Long> paymentIdList = sheetAmountDetailGetDTO.getPaymentIdList();
        if (sheetAmountDetailGetDTO.getEmptyFilterFlag()) {
            if (paymentIdList == null) {
                paymentIdList = new ArrayList<>();
            }
            paymentIdList.add(-1L);
        }
        if (CollectionUtils.isNotEmpty(paymentIdList)) {
            ConditionsEntityExt paymentIdConditions = new ConditionsEntityExt(BasicConstant.ID, "", FieldTypeEnum.DATAID.getType(), ConditionEnum.IN.getSymbol(), new ArrayList<>(paymentIdList));
            conditionList.add(paymentIdConditions);
        }
        amountDetailGetPojo.setConditionList(conditionList);
        return amountDetailGetPojo;
    }

    /**
     * 根据关联id和条件获取数据
     * @param amountDetailGetPojo 获取金额明细时需要用到的条件、原单信息pojo
     * @param sheetAmountDetailGetDTO dto
     * @param explainMap 解释map（排序时用到）
     * @param sortMap 排序map
     * @return AmountDetailGetPojo
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private List<PaasFormDataEntityExt> getPlanList4AmountDetailGet(AmountDetailGetPojo amountDetailGetPojo, SheetAmountDetailGetDTO sheetAmountDetailGetDTO, Map<String, FieldAttrEntity> explainMap, Map<String, String> sortMap) throws XbbException {
        //查询出部分字段（其他id、dataId等字段内部会强制加上去，无需重复置入）
        List<String> fieldList = Arrays.asList(PayPlanEnum.LINK_PURCHASE.getAttr(), PayPlanEnum.UN_AMOUNT.getAttr(), PayPlanEnum.REAL_AMOUNT.getAttr());
        FormDataListDTO formDataListDTO = new FormDataListDTO();
        BeanUtil.copyProperties(sheetAmountDetailGetDTO, formDataListDTO);
        formDataListDTO.setAppId(null);
        formDataListDTO.setFormId(null);
        formDataListDTO.setDel(DelEnum.NORMAL.getDel());
        formDataListDTO.setConditions(amountDetailGetPojo.getConditionList());
        formDataListDTO.setBusinessType(XbbRefTypeEnum.PAY_PLAN.getCode());
        formDataListDTO.setSortMap(sortMap);
        formDataListDTO.setExplainMap(explainMap);
        formDataListDTO.setFieldList(fieldList);
        formDataListDTO.setSearchSubForm(false);
        PaasFormDataEsListVO paasFormDataEsListVO = paasFormDataEsModel.list(formDataListDTO, IndexTypeEnum.IDX_SAAS_PAY_PLAN);
        PageHelper pageHelper = paasFormDataEsListVO.getPageHelper();
        if (pageHelper.getRowsCount() > SizeConstant.PAGE_SIZE) {
            //查询采购合同下的付款计划时，限制只能查询1w条
            throw new XbbException(PayErrorCodeEnum.API_ERROR_226017, PayErrorCodeEnum.API_ERROR_226017.getMsg(), SizeConstant.PAGE_SIZE);
        }
        return paasFormDataEsListVO.getPaasFormDataESList();
    }

    /**
     * 除了计算总的未付/已付金额，还要处理封装明细-按顺序封装明细内所有东西：签订人、金额、采购合同编号、付款计划编号等
     * 注：最后封装的明细排序规则为：（1）第一层按照前端传入的采购合同id顺序（dataIdList的顺序）；（2）第二层内层按照付款计划预计付款时间升序-红冲则直接按照原单的明细进行渲染
     * @param isRedFlag 是否红冲，如果红冲则读取关联原始付款单相关及其子单（红冲预付款不会关联采购合同和付款计划，不会调用该接口）；否则读取付款计划的未付款
     * @param sheetAmountDetailGetDTO 获取付款单明细dto
     * @param dataIdList 采购合同id集合
     * @param payPlanEnum 未付/实付字段
     * @param purchaseEnum 未付/已付字段
     * @param corpid 公司id
     * @return SheetAmountDetailGetVO
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private SheetAmountDetailGetVO getResult4AmountDetail(String corpid, boolean isRedFlag, SheetAmountDetailGetDTO sheetAmountDetailGetDTO, List<Long> dataIdList, PayPlanEnum payPlanEnum, PurchaseEnum purchaseEnum) throws XbbException {
        //封装查询付款计划（未付或已付）的条件，如果是红冲的，只需要获得关联的原始付款单
        AmountDetailGetPojo amountDetailGetPojo = getPaymentAmountDetailGetPojo(isRedFlag, sheetAmountDetailGetDTO);
        SheetAmountDetailGetVO sheetAmountDetailGetVO = new SheetAmountDetailGetVO();
        //采购合同id-该合同的付款计划/付款单list（把同一个采购合同的付款计划/付款单放到一起）
        Map<Long, List<JSONObject>> resultMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (isRedFlag) {
            //创建红冲付款单，则只需读取到关联原始付款单，及其子单，然后明细通过其本身或子单进行渲染（只能红冲原单的已付）
            PaasFormDataEntityExt linkSheetEntity = amountDetailGetPojo.getLinkSheetEntity();
            //确保每个数据关联的采购合同、付款计划都是一条（因为sheetList内只塞入子、及无子的付款单，而这类付款单都是只关联一个）
            List<PaasFormDataEntityExt> sheetList = new ArrayList<>();
            if (Objects.equals(linkSheetEntity.getAlone(), SheetAloneEnum.PARENT.getCode())) {
                //查询出所有子付款单
                sheetList.addAll(fundHelp.getChildEsDataListByParentUuid(IndexTypeEnum.IDX_SAAS_PAYPLAN_SHEET, corpid, linkSheetEntity.getUuid(), null));
            } else {
                //无父子回款单则塞入本身
                sheetList.add(linkSheetEntity);
            }
            /**
             key：红冲付款单的采购合同id_付款计划id
             value：红冲单据的金额
             */
            Map<String, Double> redCidPidAmountMap = saasPaySheetHelper.handleRedSheetsBySheet(corpid, sheetAmountDetailGetDTO.getLinkPreId(), amountDetailGetPojo);
            Set<String> belongUserSet = new HashSet<>();
            for (PaasFormDataEntityExt entityExt : sheetList) {
                JSONArray belongArr = FastJsonHelper.getJsonArrOrDefaultFromFormData(entityExt.getData(), PaySheetEnum.BELONG_ID.getAttr(), new JSONArray());
                if (belongArr.size() > 0) {
                    List<String> belongList = belongArr.toJavaList(String.class);
                    belongUserSet.addAll(belongList);
                }
            }
            //归属人userId-归属人姓名
            Map<String, String> belongNameMap = commonHelp.getUserNameMapByIds(corpid, belongUserSet);
            for (PaasFormDataEntityExt entityExt : sheetList) {
                JSONObject data = entityExt.getData();
                String paySheetType = FastJsonHelper.getStringFromFormData(data, PaySheetEnum.PAY_SHEET_TYPE.getAttr());
                JSONArray idArr = FastJsonHelper.getJsonArrFromFormData(data, PaySheetEnum.LINK_PURCHASE.getAttr());
                if (idArr == null || idArr.isEmpty()) {
                    continue;
                }
                JSONArray contractNoArr = FastJsonHelper.getJsonArrOrDefaultFromFormData(data, PaySheetEnum.PURCHASE_LINK_TEXT.getAttr(), new JSONArray());
                //都是一条（因为sheetList内只塞入子、及无子的付款单，而这类付款单都是只关联一个）
                Long contractId = idArr.getLong(0);
                String contractSerialNo = (contractNoArr.size() <= 0) ? "" : contractNoArr.getString(0);
                JSONArray belongArr = FastJsonHelper.getJsonArrOrDefaultFromFormData(data, PaySheetEnum.BELONG_ID.getAttr(), new JSONArray());
                //红冲付款单直接用原单的归属人
                String belongUserId = (belongArr.size() <= 0)? "": belongArr.getString(0);
                String belongName = belongNameMap.getOrDefault(belongUserId, "");
                //付款计划信息
                List<Long> pidList = FastJsonHelper.getJsonArrOrDefaultFromFormData(data, PaySheetEnum.PAY_PLAN.getAttr(), new JSONArray()).toJavaList(Long.class);
                List<IdNamePojo> paymentInfo = null;
                String key = contractId + "";
                if (pidList.size() > 0) {
                    Long paymentId = pidList.get(0);
                    key += "_" + paymentId;
                    JSONArray nameArr = FastJsonHelper.getJsonArrFromFormData(data, PaySheetEnum.PAY_PLAN_LINK_TEXT.getAttr());
                    String paymentSerialNo = nameArr.getString(0);
                    paymentInfo = Collections.singletonList(new IdNamePojo(paymentId, paymentSerialNo));
                }
                double sheetAmount = FundHelp.getSheetAmount4PaySheet(data, paySheetType);
                //关联了采购合同、付款计划的明细红冲金额，此时为负值
                Double sheetRedAmount = redCidPidAmountMap.getOrDefault(key, 0D);
                Double left = Arith.add(sheetAmount, sheetRedAmount);
                if (left == 0) {
                    continue;
                }
                JSONObject jsonObject = sheetAmountDetailGetVO.formatInfo4Pay(contractId, contractSerialNo, belongUserId, belongName, paymentInfo, left, left);
                List<JSONObject> list = resultMap.getOrDefault(contractId, new ArrayList<>());
                list.add(jsonObject);
                resultMap.put(contractId, list);
            }
            //最后封装的明细排序规则为：按照前端传入的采购合同id顺序（dataIdList的顺序）
            List<JSONObject> list = new ArrayList<>();
            for (Long contractId : dataIdList) {
                if (resultMap.get(contractId) != null) {
                    list.addAll(resultMap.get(contractId));
                }
            }
            fundHelp.subRedAmount(XbbRefTypeEnum.PAY_SHEET, sheetAmountDetailGetVO, amountDetailGetPojo);
            sheetAmountDetailGetVO.setList(list);
        } else {
            //非红冲，则读取付款计划的未付
            //付款计划排序，内部排序方法需要用到explainMap
            PaasFormExplainEntity paasFormExplainEntity = paasFormExplainModel.getByBusinessType(RedundantTemplateTypeEnum.PAY_PLAN.getCode(), corpid);
            if (Objects.isNull(paasFormExplainEntity)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_EXPLAIN_NOT_EXIST);
            }
            //排序字段：预计付款时间升序
            Map<String, String> sortMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            sortMap.put("field", PayPlanEnum.ESTIMATE_TIME.getAttr());
            sortMap.put("sort", SortOrder.ASC.toString());
            Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(paasFormExplainEntity.getExplains(), null);
            //得到付款计划（未付/已付）查询结果：需对计划排序，因此需要explainMap、sortMap
            List<PaasFormDataEntityExt> planList = getPlanList4AmountDetailGet(amountDetailGetPojo, sheetAmountDetailGetDTO, explainMap, sortMap);
            //合同id-签订人userId
            Map<Long, String> cidSignPersonMap = new HashMap<>(dataIdList.size());
            //合同id-合同信息
            Map<Long, PaasFormDataEntityExt> cidMap = new HashMap<>(dataIdList.size());
            //签订人userId-签订人姓名
            Map<String, String> signNameMap = handleContractInfo(corpid, dataIdList, cidSignPersonMap, cidMap);
            String attrPlan = payPlanEnum.getAttr();
            String attrContract = purchaseEnum.getAttr();
            String attrContract4Plan = PayPlanEnum.LINK_PURCHASE.getAttr();
            //封装最终结果：（1）第一层按照前端传入的采购合同id顺序（dataIdList的顺序）；（2）第二层内层按照付款计划预计付款时间升序-红冲则直接按照原单的明细进行渲染
            fundHelp.formatDetailResult(false, sheetAmountDetailGetVO, dataIdList, resultMap, planList, cidMap, cidSignPersonMap, signNameMap, attrPlan, attrContract, attrContract4Plan);
        }
        return sheetAmountDetailGetVO;
    }

    /**
     * 查询采购合同信息，并处理构建采购合同id和采购合同一些信息的对应关系map，以及签订人userId和签订人名称对应关系map
     * @param corpid 公司id
     * @param dataIdList 采购合同id集合
     * @param cidSignPersonMap 采购合同id-签订人userId
     * @param cidMap 采购合同id-合同信息
     * @return java.util.Map<java.lang.String,java.lang.String>
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private Map<String, String> handleContractInfo(String corpid, List<Long> dataIdList, Map<Long, String> cidSignPersonMap, Map<Long, PaasFormDataEntityExt> cidMap) throws XbbException {
        List<String> fieldList = Arrays.asList("id", PurchaseEnum.SHEET_NO.getAttr(), PurchaseEnum.getAttrConnectData(PurchaseEnum.TOTAL_MONEY), PurchaseEnum.getAttrConnectData(PurchaseEnum.FINISH_AMOUNT), PurchaseEnum.getAttrConnectData(PurchaseEnum.UN_FINISH_AMOUNT), PurchaseEnum.getAttrConnectData(PurchaseEnum.SIGNER_ID));
        List<PaasFormDataEntityExt> contracts = formHelp.getFormListByIds4All(corpid, IndexTypeEnum.IDX_SAAS_PURCHASE, dataIdList, fieldList);
        Set<String> signUserIds = new HashSet<>();
        for (PaasFormDataEntityExt entityExt : contracts) {
            String signUserId = FastJsonHelper.getStringOrDefaultFromFormData(entityExt.getData(), PurchaseEnum.SIGNER_ID.getAttr(), "-1");
            signUserIds.add(signUserId);
            cidSignPersonMap.put(entityExt.getId(), signUserId);
            cidMap.put(entityExt.getId(), entityExt);
        }
        //签订人userId-签订人姓名
        return commonHelp.getUserNameMapByIds(corpid, signUserIds);
    }

    /**
     * 判断已付金额是否已经超过付款计划内的未付金额
     *
     * @param sheetTypeEnum
     * @param dataList
     * @param corpid
     * @param paymentIdToAmount
     * @param paymentIds
     * @return
     */
    public void checkPlan(SheetTypeEnum sheetTypeEnum, JSONObject dataList, String corpid, Map<Long, Double> paymentIdToAmount, Set<Long> paymentIds) throws XbbException {
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("idIn", paymentIds);
        param.put("corpid", corpid);
        List<PayPlanEntityExt> payments = payPlanModel.findEntitys(param);
        if (Objects.isNull(payments)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_DATA_NOT_EXIST);
        }
        boolean isRedFlag = SheetTypeEnum.isRed(sheetTypeEnum);
        boolean isBadFlag = SheetTypeEnum.isBad(sheetTypeEnum);
        for (PayPlanEntityExt entityExt : payments) {
            String name = entityExt.getSerialNo();
            if (Objects.equals(entityExt.getDel(), DelEnum.DELETE.getDel())) {
                throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208032, PaymentErrorCodeEnum.API_ERROR_208032.getMsg(), XbbRefTypeEnum.PAY_PLAN.getName(), name);
            }
            Long paymentId = entityExt.getId();
            //当前付款计划的实付金额(新增)
            Double value = paymentIdToAmount.getOrDefault(paymentId, 0D);
            if (value == 0) {
                continue;
            }
            JSONObject data = entityExt.getData();
            Double tempAmount = value;
            Double planRealAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, PayPlanEnum.REAL_AMOUNT.getAttr(), 0D);
            Double unAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, PayPlanEnum.UN_AMOUNT.getAttr(), 0D);
            if (isRedFlag) {
                if (Arith.sub(tempAmount , planRealAmount) > ConfigConstant.amountAccuracy) {
                    //红冲，限制红冲金额小于等于付款金额
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.DETAIL_BEYOND_AMOUNT_RED, XbbRefTypeEnum.PAY_PLAN.getName() + name);
                }
            } else {
                //普通付款（已核销、预付款核销、坏账），限制付款金额小于等于未付金额
                if (Arith.sub(value , unAmount) > ConfigConstant.amountAccuracy) {
                    if (isBadFlag) {
                        throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.DETAIL_BEYOND_AMOUNT_BAD, XbbRefTypeEnum.PAY_PLAN.getName(), name);
                    }
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.DETAIL_BEYOND_AMOUNT_PAY, XbbRefTypeEnum.PAY_PLAN.getName(), name);
                }
            }
        }
    }

    /**
     * 处理付款计划并封装要更新的结果：并把付款计划的已付金额增加及更改其付款状态等
     *
     * @param sheetTypeEnum
     * @param sheetData
     * @param corpid
     * @param paymentIdToAmount
     * @param paymentIds
     * @return
     */
    public List<UpdateDataEntity> handlePlan(SheetTypeEnum sheetTypeEnum, JSONObject sheetData, String corpid, Map<Long, Double> paymentIdToAmount, Set<Long> paymentIds) throws XbbException {
        Long sheetPaymentTime = sheetData.getLongValue(PaySheetEnum.REAL_PAY_TIME.getAttr());
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("idIn", paymentIds);
        param.put("corpid", corpid);
        List<PayPlanEntityExt> payments = payPlanModel.findEntitys(param);
        List<UpdateDataEntity> updateList = new ArrayList<>();
        boolean isRedFlag = SheetTypeEnum.isRed(sheetTypeEnum);
        boolean isBadFlag = SheetTypeEnum.isBad(sheetTypeEnum);
        for (PayPlanEntityExt entityExt : payments) {
            Long paymentId = entityExt.getId();
            //当前付款计划的实付金额(新增)
            Double value = paymentIdToAmount.getOrDefault(paymentId, 0D);
            if (value == 0) {
                continue;
            }
            JSONObject data = entityExt.getData();
            Double tempAmount = value;
            Double planRealAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, PayPlanEnum.REAL_AMOUNT.getAttr(), 0D);
            Double unAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, PayPlanEnum.UN_AMOUNT.getAttr(), 0D);
            JSONObject updateData = new JSONObject();
            if (isRedFlag) {
                //红冲的，把金额置为负数。用tempAmount计算金额是否合法
                value = Arith.mul(value, -1);
            } else {
                if (!isBadFlag) {
                    //坏账，红冲不需要置付款计划的实际付款日期，只有已核销、预付款核销才需要
                    Long realPayTime = data.getLongValue(PayPlanEnum.REAL_PAY_TIME.getAttr());
                    if (realPayTime < sheetPaymentTime) {
                        updateData.put(PayPlanEnum.REAL_PAY_TIME.getAttr(), sheetPaymentTime);
                    }
                }
            }
            //处理付款计划的已付、未付、付款状态
            Double planAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, PayPlanEnum.AMOUNT.getAttr(), 0D);
            Double badAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, PayPlanEnum.BAD_AMOUNT.getAttr(), 0D);
            if ( isRedFlag ) {
                updateData.put(PayPlanEnum.HAS_RED.getAttr(), HasRedEnum.HAS_RED.getCode());
            }
            handlePlanAmountStatus4Add(isBadFlag, value, updateData, planAmount, planRealAmount, unAmount, badAmount);
            UpdateDataEntity updateDataEntity = ExplainUtil.getUpdateData(paymentId, updateData, corpid);
            updateList.add(updateDataEntity);
        }
        return updateList;
    }

    /**
     * 处理付款计划的已付、未付、付款状态：付款金额增加的情况（已核销、预付款核销、红冲-需确保金额为负数）
     * 注：坏账的优先级最高，如果有坏账金额，则付款状态要么是坏账、要么是部分坏账
     * @param isBadFlag true坏账，false非坏账。当是坏账时，付款计划的状态直接置为坏账
     * @param amount 新传入的付款金额
     * @param updateData 要更新的data
     * @param planAmount 计划内的付款金额
     * @param planRealAmount 计划内的已付金额
     * @param unAmount 计划内的未付金额
     * @param badAmount 坏账金额
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void handlePlanAmountStatus4Add(boolean isBadFlag, Double amount, JSONObject updateData, Double planAmount, Double planRealAmount, Double unAmount, Double badAmount) {
        //未付减去amount，如果amount为负数，则为加
        unAmount = Arith.sub(unAmount, amount);
        String planStatus;
        if (isBadFlag) {
            if (Objects.equals(amount, planAmount)) {
                //坏账金额等于付款计划金额，则全部坏账，否则部分坏账
                planStatus = PayPlanStatusEnum.BAD_DEBT.getCode();
            }else{
                planStatus = PayPlanStatusEnum.PARTIAL_BAD_DEBT.getCode();
            }
            updateData.put(PayPlanEnum.BAD_AMOUNT.getAttr(), amount);
        } else {
            planStatus = handleStatus(planAmount, unAmount, badAmount);
            //已付加上amount（坏账不需要加实付：坏账是坏的未付，只对未付金额减去坏账金额即可）
            amount = Arith.add(planRealAmount, amount);
            updateData.put(PayPlanEnum.REAL_AMOUNT.getAttr(), amount);
        }
        updateData.put(PayPlanEnum.STATUS.getAttr(), planStatus);
        updateData.put(PayPlanEnum.UN_AMOUNT.getAttr(), unAmount);
    }

    public String handleStatus(Double planAmount, Double unAmount, Double badAmount) {
        String planStatus;
        if (Objects.equals(badAmount, planAmount)) {
            //坏账金额等于付款计划金额，则全部坏账，否则部分坏账
            planStatus = PayPlanStatusEnum.BAD_DEBT.getCode();
        } else if (badAmount > 0) {
            planStatus = PayPlanStatusEnum.PARTIAL_BAD_DEBT.getCode();
        } else if (unAmount <= 0) {
            planStatus = PayPlanStatusEnum.RECEIVABLES.getCode();
        } else if (unAmount < planAmount) {
            planStatus = PayPlanStatusEnum.PARTIAL_RECEIPTS.getCode();
        } else {
            planStatus = PayPlanStatusEnum.UN_RECEIVABLES.getCode();
        }
        return planStatus;
    }

    /**
     * 新建付款单时，同步更新付款计划
     * @param corpid 公司id
     * @param updateList 要更新的付款计划list
     * @param errorMsg 错误信息
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public void savePayPlan(String corpid, List<UpdateDataEntity> updateList, String errorMsg) throws XbbException {
        try {
            if (updateList.size() > 0) {
                payPlanModel.updateBatch(updateList, corpid);
            }
        } catch (Exception e) {
            LOG.error(errorMsg, e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
    }
}
